<?php

// Namespace

namespace Loop;


// Dependencies

require_once(PATH_LOOP . 'Class/CView.php');
require_once(PATH_LOOP . 'Class/CFileHelper.php');
require_once(PATH_LOOP . 'Class/CTimeHelper.php');


/**
 * CViewManager is a class that takes care of rendering views
 */

class CViewManager
{
	// Properties
	
	private $rootView = null;
	private $views = null;


	/**
	 * Constructs a view manager
	 */

	public function __construct()
	{
		$this->views = array();
	}

	
	/**
	 * Clears the output buffer
	 */
	
	public function clearBuffer()
	{
		// Clean the buffer
		
		$handlers = ob_list_handlers();
		
		while (empty($handlers) == false)
		{
			ob_end_clean();
			$handlers = ob_list_handlers();
		}
	}


	/**
	 * Creates the view from command lines arguments
	 * @todo For now it only supports APIs, should also work with views
	 *
	 * @return	object A view
	 */

	 public function createViewFromArguments()
	 {
		// Check if we get some args

		$apiUri = fv('api', true);


		// Get the fragments to create the view	

		$pattern = '/^';
		$pattern .= '(\[([a-zA-Z\-]+)\]\/)?';
		$pattern .= '([a-zA-Z_]+\.[a-zA-Z]+)';
		$pattern .= '$/';
		
		if (preg_match($pattern, $apiUri, $matches) == false)
		{
			return;
		}

		
		// Fill-in the fragments
		
		$fragments = array();
		
		$fragments['extensionId'] = $matches[2];
		$fragments['viewType'] = VIEW_TYPE_API;
		$fragments['viewClass'] = null;
		$fragments['viewName'] = $matches[3];
		$fragments['viewFileExtension'] = 'json';
		

		// Create the view
		
		$view = $this->createViewFromProperties
		(
			$fragments['extensionId'],
			$fragments['viewType'],
			$fragments['viewClass'],
			$fragments['viewName'],
			$fragments['viewFileExtension']
		);
		
		
		return $view;
	 }
	
	
	/**
	 * Renders a view from the view path
	 *
	 * @param	string $viewPath The path to the view (e.g. (Loop/)Html/Page.Home)
	 *
	 * @return	object A view
	 */
	
	public function createViewFromPath($viewPath)
	{
		// Get path fragments
		
		$viewFragments = app()->getPathHelper()->convertPathToFragments($viewPath);
		
		
		// Create the view
		
		$view = $this->createViewFromProperties
		(
			$viewFragments['extensionId'],
			$viewFragments['viewType'],
			$viewFragments['viewClass'],
			$viewFragments['viewName'],
			$viewFragments['viewFileExtension']
		);
		
		
		return $view;
	}
	
	
	/**
	 * Renders a view from the view properties
	 *
	 * @return	object A view
	 */
	
	public function createViewFromProperties($viewExtensionId, $viewType, $viewClass, $viewName, $viewFileExtension)
	{
		// Create the view

		$view = new \Loop\CView($viewExtensionId, $viewType, $viewClass, $viewName, $viewFileExtension);
		
		if (is_null($this->rootView) == true)
		{
			// We have a root view!
			
			$this->setRootView($view);
		}
		
		
		return $view;
	}
	
	
	/**
	 * Renders a root view from a URI
	 *
	 * @param	string $uri The URI (e.g. /en/page/home/)
	 *
	 * @return	object A view
	 */
	
	public function createViewFromUri($uri)
	{
		
		// First of all, decode the URI and look for flags
		
		$uri = urldecode($uri);
		
		$flags = null;
		$positionOfFlags = strpos($uri, '?');
		
		if ($positionOfFlags !== false)
		{
			$flags = substr($uri, $positionOfFlags);
			$uri = substr($uri, 0, $positionOfFlags);
		}
		
		
		// If URI is empty, redirect to home page
		
		if
		(
			(empty($uri) == true) ||
			($uri == '/')
		)
		{
			redirect(pref('[Loop]/localization/languageIsoCode') . '/' . app()->getPathHelper()->convertPathToUri(pref('[Loop]/views/pathToPageHome.html')) . $flags);
			return;
		}
		
		
		// Extract fragments from the URI
		
		$viewFragments = app()->getPathHelper()->convertUriToFragments($uri);
		
		
		// Do we have a language in the URL?
		
		if (empty($viewFragments['languageIsoCode']) == false)
		{
			// Set the language
			
			pref('[Loop]/localization/languageIsoCode', $viewFragments['languageIsoCode']);
		}
		

		// Create the view
		
		$view = $this->createViewFromProperties
		(
			$viewFragments['extensionId'],
			$viewFragments['viewType'],
			$viewFragments['viewClass'],
			$viewFragments['viewName'],
			$viewFragments['viewFileExtension']
		);


		// Set variables, if any

		if (isset($viewFragments['viewVariables']) == true)
		{
			$view->setVariables($viewFragments['viewVariables']);
		}
		
		
		return $view;
	}
	
	
	/**
	 * Accessor to get the current view
	 *
	 * @return	CView the current view
	 */
	
	public function getCurrentView()
	{
		// Get the latest view
		
		$view = array_pop($this->views);
		array_push($this->views, $view);
		
		
		return $view;
	}
	
	
	/**
	 * Accessor to get the root view
	 *
	 * @return	CView the root view
	 */
	
	public function getRootView()
	{
		return $this->rootView;
	}
	
	
	/**
	 * Initializes the view manager (creates the root view)
	 */
	
	public function initialize()
	{
		if (app()->isRunningCli() == true)
		{
			$this->setRootView($this->createViewFromArguments());
		}
		else
		{
			$this->setRootView($this->createViewFromUri(v('SERVER::REQUEST_URI')));
		}
	}
	
	
	/**
	 * Renders the root view
	 */
	
	public function renderRootView()
	{
		print $this->renderView($this->rootView);
	}
	
	
	/**
	 * Renders a view
	 *
	 * @param	object 	$view			A reference to the view
	 * @param	array	$viewVariables 	An array containing variables for the view
	 * @param	bool	$printResult	True to print the result, false to simply return it
	 *
	 * @return	string The output
	 */
	
	public function renderView(&$view, $viewVariables = null)
	{
		//

		$puuid = pstart(PROFILING_TYPE_VIEW, $view->getPath());


		// Set the view variables, if any
		
		if (empty($viewVariables) == false)
		{
			$view->setVariables($viewVariables);
		}
		

		// Try to render the cache, if enabled

		$useCache = fv('useCache');
		$useGlobalCache = fv('useGlobalCache');

		if
		(
			(pref('[Loop]/features/debug') == false) &&
			($useCache == true) &&
			($view->isRoot == true)
		)
		{
			try
			{
				// Render!

				$output = $view->renderCache($useGlobalCache);


				// Send HTTP headers

				$view->sendHttpHeaders();


				// Return output

				return $output;
			}
			catch (\Exception $e)
			{
				if ($e->getCode() != EXCEPTION_VIEW_SERVER_CACHE_NOT_AVAILABLE)
				{
					throw $e;
				}
			}
		}


		// Start buffering output
		
		ob_start();
		

		// If in debug mode, make sure we display fatal errors
		
		if (pref('[Loop]/features/debug') == true)
		{
			error_reporting(pref('[Loop]/exceptions/level'));
			ini_set('error_reporting', pref('[Loop]/exceptions/level'));
			ini_set('display_errors', 'on');
		}
			

		// Render the view
		
		if ($view->isRoot == false)
		{
			// If not root, simply render the child view
			
			$view->render();
		}
		else if ($view->type == VIEW_TYPE_ACTION)
		{
			// Render the view
			
			$result = $view->renderApi();
			
			
			// Do we have a URL of redirection?

			$url = null;

			if (isset($result['urlOfRedirection']) == true)
			{
				$url = $result['urlOfRedirection'];
			}


			// Redirect!
			
			redirect($url);
		}
		else if ($view->type == VIEW_TYPE_API)
		{
			// Render the view
			
			$apiResult = $view->renderApi();
			
			
			// Convert results into XML or JSON
			
			$view = $this->createViewFromProperties('Loop', ucfirst($view->fileExtension), 'Snippet', 'Api.Result', $view->fileExtension);

			$viewVariables = array('apiResult' => $apiResult);
			$view->setVariables($viewVariables);

			$view->render();

			
			// The api result view is set to root

			$this->setRootView($view);
		}
		else if ($view->class == 'Page')
		{
			// Try to render the view in a separate output buffer

			$exceptions = array();
			$viewOutput = null;

			try
			{
				ob_start();

				$view->render();
				$viewOutput = ob_get_contents();

				ob_end_clean();
			}
			catch (\Exception $e)
			{
				$exceptions[] = $e;
			}

			
			// Try to render the header
			
			try
			{
				$childView = $this->createViewFromPath(pref('[Loop]/views/pathToSnippetHeader.' . $view->fileExtension));
				$childView->render();
			}
			catch (\Exception $e)
			{
				if ($e->getCode() != EXCEPTION_VIEW_NOT_FOUND)
				{
					$exceptions[] = $e;
				}
			}
			
			
			// Print the view output
			
			print $viewOutput;
			
			
			// Try to render the footer
			
			try
			{
				$childView = $this->createViewFromPath(pref('[Loop]/views/pathToSnippetFooter.' . $view->fileExtension));
				$childView->render();
			}
			catch (\Exception $e)
			{
				if ($e->getCode() != EXCEPTION_VIEW_NOT_FOUND)
				{
					$exceptions[] = $e;
				}
			}


			// Did the view generate any exception?

			if (empty($exceptions) == false)
			{
				throw $exceptions[0];
			}
		}
		else
		{
			// Render the view
			
			$view->render();
		}


		// Always add an empty line in CLI mode

		if (app()->isRunningCli() == true)
		{
			print "\n";
		}

		
		// Get output
		
		$output = ob_get_contents();
		

		// Save the cache, if enabled

		if
		(
			(pref('[Loop]/features/debug') == false) &&
			($useCache == true) &&
			($view->isRoot == true)
		)
		{
			$view->saveCache($output, $useGlobalCache);
		}

		
		// Send HTTP headers
		
		$view->sendHttpHeaders();


		// Clean the buffer
		
		ob_end_clean();

		
		//

		pstop($puuid);


		return $output;
	}

	
	/**
	 * Sets the root view
	 *
	 * @param	CView 	$rootView 	The view to be set as root
	 */
	
	public function setRootView($rootView)
	{
		// Make sure the previous root view is no longer root
		
		if (is_null($this->rootView) == false)
		{
			$this->rootView->isRoot = false;
		}
		
		
		// Set the view to the root view
		
		$this->rootView = $rootView;
		$this->rootView->isRoot = true;
	}
	
	
	/**
	 * Sets the given view to the current view
	 *
	 * @param	CView 	$view 	The view to be set to the current view
	 */
	
	public function startCurrentView($view)
	{
		// Set the view to the current view
		
		array_push($this->views, $view);
	}
	
	
	/**
	 * Removes the current view
	 */
	
	public function stopCurrentView()
	{

		array_pop($this->views);
	}
}

?>
